package com.azzcs.spring;

import com.azzcs.spring.anno.Autowired;
import com.azzcs.spring.anno.Component;
import com.azzcs.spring.anno.Controller;
import com.azzcs.spring.anno.Service;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @Author: wzg
 * @Date: 2020/12/28 下午5:19
 */
public class ApplicationContent {

    Map<String,Object> applicationContent = new HashMap<>();
    private List<String> classNames = new ArrayList<>();
    private List<String> beanNames = new ArrayList<>();
    private Map<Class,List<String>> typeBeanNames = new HashMap<>();
    public ApplicationContent(String... basePages) {
        initApplicationContent(basePages);
    }

    private void initApplicationContent(String... basePages) {
        doScan(basePages);
        doInstance();
        doAutowired();
    }

    public List<String> getBeanNames() {
        return beanNames;
    }

    public Object getBean(String beanName){
        return applicationContent.get(beanName);
    }

    private void doScan(String... basePages) {
        for (String scanPackage : basePages) {
            String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", File.separator);
            File pack = new File(scanPackagePath);

            File[] files = pack.listFiles();
            if(files == null){
                continue;
            }
            for(File file: files) {
                if(file.isDirectory()) { // 子package
                    // 递归
                    doScan(scanPackage + "." + file.getName());
                }else if(file.getName().endsWith(".class")) {
                    String className = scanPackage + "." + file.getName().replaceAll(".class", "");
                    classNames.add(className);
                }
            }
        }
    }


    private void doInstance()  {

        if(classNames.size() == 0) return;

        try{

            for (int i = 0; i < classNames.size(); i++) {
                String className =  classNames.get(i);  // com.lagou.demo.controller.DemoController

                // 反射
                Class<?> aClass = Class.forName(className);
                // 区分controller，区分service'
                if(aClass.isAnnotationPresent(Controller.class)) {
                    // controller的id此处不做过多处理，不取value了，就拿类的首字母小写作为id，保存到ioc中
                    String simpleName = aClass.getSimpleName();// DemoController
                    String lowerFirstSimpleName = lowerFirst(simpleName); // demoController
                    Object o = aClass.newInstance();
                    addBean(lowerFirstSimpleName,o);
                }else if(aClass.isAnnotationPresent(Service.class)||aClass.isAnnotationPresent(Component.class)) {
                    Service annotation = aClass.getAnnotation(Service.class);
                    String beanName = null;
                    if(annotation == null){
                        Component component = aClass.getAnnotation(Component.class);
                        beanName = component.value();
                    }else {
                        beanName = annotation.value();
                    }


                    // 如果指定了id，就以指定的为准
                    if(!"".equals(beanName.trim())) {
                        addBean(beanName,aClass.newInstance());
                    }else{
                        // 如果没有指定，就以类名首字母小写
                        beanName = lowerFirst(aClass.getSimpleName());
                        addBean(beanName,aClass.newInstance());
                    }

//                    // service层往往是有接口的，面向接口开发，此时再以接口名为id，放入一份对象到ioc中，便于后期根据接口类型注入
//                    Class<?>[] interfaces = aClass.getInterfaces();
//                    for (int j = 0; j < interfaces.length; j++) {
//                        Class<?> anInterface = interfaces[j];
//                        // 以接口的全限定类名作为id放入
//                        beanName = anInterface.getName();
//                        addBean(beanName,aClass.newInstance());
//                    }
                }else{
                    continue;
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void addBean(String name, Object bean) {
        if(applicationContent.containsKey(name)){
            return;
        }
        applicationContent.put(name,bean);
        beanNames.add(name);
        addTypeBeanNames(name,bean.getClass());
    }

    private void addTypeBeanNames(String name, Class clazz) {
        List<String> list = typeBeanNames.get(clazz);
        if(list == null){
            list = new ArrayList<>();
            typeBeanNames.put(clazz,list);
        }
        list.add(name);
        Class<?>[] interfaces = clazz.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            addTypeBeanNames(name,anInterface);
        }
    }

    public String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

    private void doAutowired() {
        if(applicationContent.isEmpty()) {return;}

        // 有对象，再进行依赖注入处理

        // 遍历ioc中所有对象，查看对象中的字段，是否有@LagouAutowired注解，如果有需要维护依赖注入关系
        for(Map.Entry<String,Object> entry: applicationContent.entrySet()) {
            // 获取bean对象中的字段信息
            Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
            // 遍历判断处理
            for (int i = 0; i < declaredFields.length; i++) {
                Field declaredField = declaredFields[i];   //  @LagouAutowired  private IDemoService demoService;
                if(!declaredField.isAnnotationPresent(Autowired.class)) {
                    continue;
                }

                // 有该注解
                Autowired annotation = declaredField.getAnnotation(Autowired.class);
                String beanName = annotation.value();  // 需要注入的bean的id
                declaredField.setAccessible(true);

                if(!"".equals(beanName.trim())){
                    try {
                        declaredField.set(entry.getValue(),applicationContent.get(beanName));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
                // 按类型注入
                Class<?> type = declaredField.getType();
                Object objects = null;
                // 如果是集合
                if(Collection.class.isAssignableFrom(type)){
                    Class clazz = getType(declaredField);
                    objects = this.getBeanByType(clazz);
                }else {
                    objects = this.getBeanByType(type);
                }
                if(objects == null){
                    throw new RuntimeException(type + "找不到 bean");
                }
                try {
                    declaredField.set(entry.getValue(),objects);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private Class getType(Field field){
        ParameterizedType parameterizedType = (ParameterizedType)field.getGenericType();
        Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
        return (Class)actualTypeArgument;
    }

    private Object getBeanByType(Class<?> type) {
        List<String> list = typeBeanNames.get(type);
        List<Object> objects = new ArrayList<>();
        for (String beanName : list) {
            objects.add(getBean(beanName));
        }
        return objects;
    }

}
